﻿using System.IO;
using System.Collections.Generic;
using UnityEngine;

public static class ModelsManager
{
    public static GameObject container;

    //*************************************************************
    public static void RemoveModel(string objectName)
    {
        if (container != null)
        {
            for (int i = 0; i < container.transform.childCount; ++i)
            {
                Transform child = container.transform.GetChild(i);
                if (child.name.Contains(objectName))
                {
                    UnityEngine.Object.Destroy(child.gameObject);
                    return;
                }
            }
        }
    }
    public static void ClearModels()
    {
        if (container != null)
        {
            container.SetActive(false);
            //UnityEngine.Object.Destroy(container);
        }
    }

    private static bool isEmpty(Vector3 space, Vector3 size)
    {
        Collider[] collidersInside = Physics.OverlapBox(space + new Vector3(0, 0.15f, 0), size / 2);
        if (collidersInside.Length > 0)
            return false;
        return true;
    }

    private static List<SelModelInfo> bufferModels;
    public static void AddModel(Info info)
    {
        // if no models then return
        if (info.models.Count == 0) return;
        // if the modelsinfo doesn't change..
        if (container != null && container.transform.childCount == info.models.Count)
        {
            //if ( && container.transform.GetChild(0).name == info.models[0].nameString )
            bool sameScene = true;
            for (int i = 0; i < container.transform.childCount; ++i)
            {
                if (container.transform.GetChild(i).name != info.models[i].nameString)
                {
                    sameScene = false;
                    break;
                }
            }
            if (sameScene)
            {
                info.models = bufferModels;
                return;
            }
        }
        // there are models.

        // 1.Load the models from ssg
        List<SelModelInfo> models = info.models;
        for (int i = 0; i < models.Count; i++)
        {
            // Give messages if not exist prefab
            if (!File.Exists("Assets/Resources/" + models[i].model_path + ".prefab"))
            {
                TextDialog.textDebug("Model " + models[i].nameString + " does not exist");
                continue;
            }

            GameObject model = (GameObject)Resources.Load(models[i].model_path, typeof(GameObject));

            info.models[i].model = model;

        }

        // 2.Place it into container
        if (container == null)
        {
            // if there is no model before: find a good place

            // create a new container and put all models into it
            container = new GameObject("Container");
            foreach (var modelInfo in info.models)
            {
                if (modelInfo.model != null)
                {
                    modelInfo.model = UnityEngine.Object.Instantiate(modelInfo.model, container.transform) as GameObject;

                    modelInfo.model.name = modelInfo.nameString;
                    modelInfo.model.transform.position = modelInfo.position;
                    modelInfo.model.transform.rotation = modelInfo.rotation;
                    modelInfo.model.transform.localScale = modelInfo.localScale;
                }
            }
        }
        else
        {
            // if there are models before: just put it here

            // temporal method: clear all models then put all in

            var pos = container.transform.position;
            var rot = container.transform.rotation;
            var scale = container.transform.localScale;

            container.SetActive(false);
            //GameObject.Destroy(container);
            container = new GameObject("Container");


            foreach (var modelInfo in info.models)
            {
                if (modelInfo.model != null)
                {
                    modelInfo.model = UnityEngine.Object.Instantiate(modelInfo.model, container.transform) as GameObject;

                    modelInfo.model.name = modelInfo.nameString;
                    modelInfo.model.transform.position = modelInfo.position;
                    modelInfo.model.transform.rotation = modelInfo.rotation;
                    modelInfo.model.transform.localScale = modelInfo.localScale;
                }
            }

            // change parent transform seems not to change childs' global position, just change their relative position
            container.transform.position = pos;
            container.transform.rotation = rot;
            container.transform.localScale = scale;

        }

        bufferModels = info.models;
        ClassifyModel(info.models);
    }

    //private Dictionary<string, int> TypeToIndex = new Dictionary<string, int>();
    private static Dictionary<string, string> NameToType = new Dictionary<string, string>();
    private static Dictionary<string, List<Interaction>> ModelType = new Dictionary<string, List<Interaction>>();

    // updateOp

    private static void GetMap(List<SelModelInfo> info) // 建立模型名称到类型的映射
    {
        NameToType = new Dictionary<string, string>();
        // Create new model mapping
        if (info != null) for (int i = 0; i < info.Count; i++)
            {
                NameToType[info[i].nameString] = info[i].modelType;
            }

        // Establish a mapping of pre-existing models
        Interaction[] models = GameObject.FindObjectsOfType<Interaction>();
        for (int i = 0; i < models.Length; i++)
        {
            if (!NameToType.ContainsKey(models[i].name))
            {
                //if (name.Equals("desk") || name.Equals("Desk"))
                //    NameToType.Add(models[i].name, "desk");
                ////else if (name.Equals("chair") || name.Equals("Chair"))
                ////	NameToType.Add(name, "chair");
                //else if (name.Equals("box") || name.Equals("Box"))
                //    NameToType.Add(models[i].name, "box");
                //else if (name.Equals("chest") || name.Equals("Chest"))
                //    NameToType.Add(models[i].name, "chest");
                //else if (name.Equals("door") || name.Equals("Door"))
                //    NameToType.Add(models[i].name, "door");
                NameToType.Add(models[i].name, models[i].name);
            }
        }
    }


    public static void ClassifyModel(List<SelModelInfo> info) // 对所有模型进行分类
    {
        GetMap(info);

        Interaction[] models = GameObject.FindObjectsOfType<Interaction>();

        // every time model change, remake the type list
        ModelType = new Dictionary<string, List<Interaction>>();

        for (int i = 0; i < models.Length; i++)
        {
            if (NameToType.ContainsKey(models[i].name))
            {
                string type = NameToType[models[i].name];

                if (!ModelType.ContainsKey(type))
                    ModelType[type] = new List<Interaction>();
                ModelType[type].Add(models[i]);

            }
        }

    }

    public static Interaction GetClosestInteraction(Transform pivot)
    {
        Interaction[] models = GameObject.FindObjectsOfType<Interaction>();
        if (models.Length == 0)
        {
            return null;
        }
        else
        {
            List<Interaction> candidates = new List<Interaction>();
            for (int i = 0; i < models.Length; i++)
            {
                candidates.Add(models[i]);
            }
            if (candidates.Count == 0)
            {
                return null;
            }
            Interaction closest = candidates[0];
            for (int i = 1; i < candidates.Count; i++)
            {
                if (Vector3.Distance(pivot.position, candidates[i].transform.position) < Vector3.Distance(pivot.position, closest.transform.position))
                {
                    closest = candidates[i];
                }
            }
            return closest;
        }
    }

    public static Interaction GetClosestModel(Transform pivot, string modelType)
    {
        if (modelType == null || !ModelType.ContainsKey(modelType))
        {
            TextDialog.textDebug("Can't find " + modelType + ", maybe lack of Interaction or wrong type", true);
            return null;

        }

        List<Interaction> models = ModelType[modelType];

        if (models.Count == 0)
            return null;

        Interaction closest = models[0];
        for (int i = 1; i < models.Count; i++)
            if (Vector3.Distance(pivot.position, models[i].transform.position) < Vector3.Distance(pivot.position, closest.transform.position))
                closest = models[i];
        return closest;
    }

    // return a gameobject maybe have no Interaction
    public static GameObject GetClosestGameObject(Transform pivot, string modelType, GameObject container)
    {
        // if container is not null, then find the object in container. else find it in the world.
        GameObject closest = null;

        if (container != null)
        {
            for (int i = 0; i < container.transform.childCount; ++i)
            {
                var child = container.transform.GetChild(i);
                if (NameToType[child.name] == modelType)
                {
                    if (closest == null || Vector3.Distance(pivot.position, child.position) < Vector3.Distance(pivot.position, closest.transform.position))
                        closest = child.gameObject;
                }
            }
        }

        if (closest != null) return closest;

        GameObject[] allObjects = GameObject.FindObjectsOfType<GameObject>();
        foreach (var obj in allObjects)
        {
            if (NameToType[obj.name].Contains(modelType))
            {
                if (closest == null || Vector3.Distance(pivot.position, obj.transform.position) < Vector3.Distance(pivot.position, closest.transform.position))
                    closest = obj;
            }
        }
        return closest;
    }
}
